package com.quant.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.quant.config.TushareConfig;
import com.quant.entity.StockBasic;
import com.quant.entity.StockDaily;
import com.quant.mapper.BacktestResultMapper;
import com.quant.mapper.StockBasicMapper;
import com.quant.mapper.StockDailyMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 数据服务类，用于处理与Tushare API交互、获取股票基础信息和日线行情数据，并进行相关计算及存储。
 *
 * @author lulj
 */
@Service
@Slf4j
public class DataService {

    @Autowired
    private TushareConfig tushareConfig;
    @Autowired
    private StockDailyMapper stockDailyMapper;
    @Autowired
    private StockBasicMapper stockBasicMapper;
	@Autowired
	private BacktestResultMapper backtestResultMapper;

    private final OkHttpClient client;
    private final ObjectMapper objectMapper;
    private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd");

	/**
	 * 构造方法：初始化OkHttpClient和ObjectMapper实例
	 */
    public DataService() {
        this.client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)
                .readTimeout(10, TimeUnit.SECONDS)
                .build();
        this.objectMapper = new ObjectMapper();
    }

    /**
     * 初始化数据库中的表结构（如果不存在则创建）
     */
    @Transactional
    public void initTables() {
        stockDailyMapper.createTableIfNotExists();
        stockBasicMapper.createTableIfNotExists();
	    backtestResultMapper.createTableIfNotExists();
        log.info("数据库表结构初始化完成");
    }

    /**
     * 获取所有上市状态为 L 的股票基本信息并批量插入到数据库中
     */
    @Transactional
    public void fetchAndSaveStockBasic() {
        try {
            // 构建请求参数
            String params = "{\"ts_code\": \"\", \"exchange\": \"\", \"list_status\": \"L\"}";
            String response = callTushareApi("stock_basic", params);

            // 解析响应
            JsonNode root = objectMapper.readTree(response);
            if (root.get("code").asInt() != 0) {
                log.error("获取股票基本信息失败: {}", root.get("msg").asText());
                return;
            }

            // 转换为实体类
            List<StockBasic> stockBasics = new ArrayList<>();
            JsonNode data = root.get("data");
            if (data != null && data.get("items") != null) {
                for (JsonNode item : data.get("items")) {
                    StockBasic basic = new StockBasic();
                    basic.setTsCode(item.get(0).asText());
                    basic.setSymbol(item.get(1).asText());
                    basic.setName(item.get(2).asText());
                    basic.setArea(item.get(3).asText());
                    basic.setIndustry(item.get(4).asText());
                    basic.setMarket(item.get(5).asText());
                    basic.setListDate(item.get(6).asText());
                    basic.setStatus(item.get(7).asText());
                    stockBasics.add(basic);
                }
            }

            // 保存到数据库
            if (!stockBasics.isEmpty()) {
                stockBasicMapper.batchInsert(stockBasics);
                log.info("保存股票基本信息 {} 条", stockBasics.size());
            }
        } catch (Exception e) {
            log.error("获取股票基本信息异常", e);
        }
    }

    /**
     * 根据指定时间范围获取某只股票的日线行情数据，并计算技术指标后存入数据库
     *
     * @param tsCode   股票代码（如：000001.SZ）
     * @param startDate 开始日期
     * @param endDate   结束日期
     */
    @Transactional
    public void fetchAndSaveStockDaily(String tsCode, LocalDate startDate, LocalDate endDate) {
        try {
            // 检查最后更新日期，避免重复获取
            LocalDate lastDate = stockDailyMapper.selectLastTradeDate(tsCode);
            LocalDate actualStartDate = (lastDate != null && lastDate.isAfter(startDate)) ? lastDate.plusDays(1) : startDate;

            if (actualStartDate.isAfter(endDate)) {
                log.info("股票 {} 数据已是最新，无需更新", tsCode);
                return;
            }

            // 构建请求参数
            String params = String.format(
                "{\"ts_code\": \"%s\", \"start_date\": \"%s\", \"end_date\": \"%s\", \"adj\": \"qfq\"}",
                tsCode,
                actualStartDate.format(dateFormatter),
                endDate.format(dateFormatter)
            );

            String response = callTushareApi("daily", params);

            // 解析响应
            JsonNode root = objectMapper.readTree(response);
            if (root.get("code").asInt() != 0) {
                log.error("获取股票 {} 日线数据失败: {}", tsCode, root.get("msg").asText());
                return;
            }

            // 转换为实体类
            List<StockDaily> dailyList = new ArrayList<>();
            JsonNode data = root.get("data");
            if (data != null && data.get("items") != null) {
                for (JsonNode item : data.get("items")) {
                    StockDaily daily = new StockDaily();
                    daily.setTsCode(item.get(0).asText());
                    daily.setTradeDate(LocalDate.parse(item.get(1).asText(), dateFormatter));
                    daily.setOpen(item.get(2).asDouble());
                    daily.setHigh(item.get(3).asDouble());
                    daily.setLow(item.get(4).asDouble());
                    daily.setClose(item.get(5).asDouble());
                    daily.setPreClose(item.get(6).asDouble());
                    daily.setChangeAmt(item.get(7).asDouble());
                    daily.setPctChg(item.get(8).asDouble());
                    daily.setVol(item.get(9).asDouble());
                    daily.setAmount(item.get(10).asDouble());
                    dailyList.add(daily);
                }
            }

            // 计算技术指标
            calculateIndicators(dailyList);

            // 保存到数据库
            if (!dailyList.isEmpty()) {
                stockDailyMapper.batchInsert(dailyList);
                log.info("保存股票 {} 日线数据 {} 条", tsCode, dailyList.size());
            }
        } catch (Exception e) {
            log.error("获取股票 {} 日线数据异常", tsCode, e);
        }
    }

    /**
     * 查询指定时间段内某只股票的日线行情数据
     *
     * @param tsCode    股票代码（如：000001.SZ）
     * @param startDate 开始日期
     * @param endDate   结束日期
     * @return 返回符合条件的股票日线数据列表
     */
    public List<StockDaily> getStockDailyData(String tsCode, LocalDate startDate, LocalDate endDate) {
        return stockDailyMapper.selectByCodeAndDateRange(tsCode, startDate, endDate);
    }

    /**
     * 对传入的股票日线数据列表计算各类技术指标（包括均线和RSI）
     *
     * @param dailyList 股票日线数据列表
     */
    private void calculateIndicators(List<StockDaily> dailyList) {
        if (dailyList.isEmpty()) return;

        // 计算均线
        calculateMovingAverages(dailyList);

        // 计算RSI
        calculateRSI(dailyList);
    }

    /**
     * 计算不同周期的移动平均线（MA）指标
     *
     * @param dailyList 股票日线数据列表
     */
    private void calculateMovingAverages(List<StockDaily> dailyList) {
        int size = dailyList.size();

        // 计算5日均线
        for (int i = 4; i < size; i++) {
            double sum = 0;
            for (int j = i - 4; j <= i; j++) {
                sum += dailyList.get(j).getClose();
            }
            dailyList.get(i).setMa5(sum / 5);
        }

        // 计算10日均线
        for (int i = 9; i < size; i++) {
            double sum = 0;
            for (int j = i - 9; j <= i; j++) {
                sum += dailyList.get(j).getClose();
            }
            dailyList.get(i).setMa10(sum / 10);
        }

        // 计算20日均线
        for (int i = 19; i < size; i++) {
            double sum = 0;
            for (int j = i - 19; j <= i; j++) {
                sum += dailyList.get(j).getClose();
            }
            dailyList.get(i).setMa20(sum / 20);
        }

        // 计算30日均线
        for (int i = 29; i < size; i++) {
            double sum = 0;
            for (int j = i - 29; j <= i; j++) {
                sum += dailyList.get(j).getClose();
            }
            dailyList.get(i).setMa30(sum / 30);
        }
    }

    /**
     * 计算RSI相对强弱指数指标
     *
     * @param dailyList 股票日线数据列表
     */
    private void calculateRSI(List<StockDaily> dailyList) {
        int size = dailyList.size();
        if (size < 2) return;

        // 计算每日涨跌幅
        double[] changes = new double[size];
        for (int i = 1; i < size; i++) {
            changes[i] = dailyList.get(i).getClose() - dailyList.get(i-1).getClose();
        }

        // 计算6日RSI
        calculateRSIForPeriod(dailyList, changes, 6, (daily, value) -> daily.setRsi6(value));

        // 计算12日RSI
        calculateRSIForPeriod(dailyList, changes, 12, (daily, value) -> daily.setRsi12(value));

        // 计算24日RSI
        calculateRSIForPeriod(dailyList, changes, 24, (daily, value) -> daily.setRsi24(value));
    }

	/**
	 * RSI设置器接口，用于统一设置不同的RSI周期值
	 */
    @FunctionalInterface
    private interface RSISetter {
        void set(StockDaily daily, Double value);
	}

	/**
	 * 计算特定周期的RSI值并赋给对应的StockDaily对象
	 *
	 * @param dailyList 股票日线数据列表
	 * @param changes   每日价格变化数组
	 * @param period    周期长度（例如6日、12日等）
	 * @param setter    设置RSI值的方法引用
	 */
    private void calculateRSIForPeriod(List<StockDaily> dailyList, double[] changes, int period, RSISetter setter) {
        int size = dailyList.size();
        double[] up = new double[size];
        double[] down = new double[size];

        // 区分上涨和下跌
        for (int i = 1; i < size; i++) {
            up[i] = Math.max(changes[i], 0);
            down[i] = Math.max(-changes[i], 0);
        }

        // 计算初始平均值
        for (int i = period; i < size; i++) {
            double sumUp = 0;
            double sumDown = 0;

            for (int j = i - period + 1; j <= i; j++) {
                sumUp += up[j];
                sumDown += down[j];
            }

            double avgUp = sumUp / period;
            double avgDown = sumDown / period;

            double rs = avgDown == 0 ? 100 : avgUp / avgDown;
            double rsi = 100 - (100 / (1 + rs));

            setter.set(dailyList.get(i), rsi);
        }
    }

    /**
     * 向Tushare API发起POST请求以获取数据
     *
     * @param apiName 接口名称（如："daily" 或 "stock_basic"）
     * @param params  请求参数JSON字符串
     * @return 返回API响应结果字符串
     * @throws IOException 当网络或IO操作出错时抛出
     */
    private String callTushareApi(String apiName, String params) throws IOException {
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        String json = String.format(
            "{\"api_name\": \"%s\", \"token\": \"%s\", \"params\": %s}",
            apiName, tushareConfig.getToken(), params
        );

        Request request = new Request.Builder()
                .url(tushareConfig.getApiUrl())
                .post(RequestBody.create(json, mediaType))
                .build();

        try (Response response = client.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("API调用失败: " + response);
            }
            return response.body().string();
        }
    }
}
